package jaux.tank.api.controller;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.PluralAttribute;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import jaux.tank.bean.vo.EntityMetadata;
import jaux.tank.bean.vo.front.Ret;
import jaux.tank.bean.vo.front.Rets;

@RestController
@RequestMapping("/api/metadata/entities/")
@Api(tags="实体元数据web接口")
public class EntitiesMetadataController {
	
	@Autowired
	EntityManager entityManager;
	
	private Set<EntityType<?>> entities;
	
	
	@PostConstruct
	public void init() {
		this.entities = entityManager.getMetamodel().getEntities();
	}
	
	@GetMapping
	@ApiOperation("查询所有实体")
	public Ret<Set<EntityMetadata>> getAll(){
		return Rets.success(this.entities.stream().map(et->convertEntityToMetadata(et)).collect(Collectors.toSet()));
	}
	
	@GetMapping("{entityName}/")
	@ApiOperation("查询指定实体")
	public Ret<EntityMetadata> getEntity(@PathVariable String entityName){
		Optional<EntityType<?>>  entityOpt = entities.stream().filter(et->entityName.equalsIgnoreCase(et.getName())).findAny();
		if(!entityOpt.isPresent())
			return Rets.failure("entity not exists");
		return Rets.success(convertEntityToMetadata(entityOpt.get()));
	}
	
	public EntityMetadata convertEntityToMetadata(EntityType<?>  et) {
		EntityMetadata em =new EntityMetadata();
		em.setName(et.getName());
		Class<?> jt = et.getJavaType();
		em.setClazz(jt.getName());
		ApiModel ama = jt.getAnnotation(ApiModel.class);
		if(ama!=null &&StringUtils.isNotBlank(ama.value()))
			em.setApiModel( ama.value());
		em.setAttrs(et.getAttributes().stream().map(attr->convertAttributeToMatedata(attr, jt)).toArray(EntityMetadata.AttributeMetadata[]::new));
		return em;
		
	}
	public EntityMetadata.AttributeMetadata convertAttributeToMatedata(Attribute<?,?>  attr, Class entityClass) {
		EntityMetadata.AttributeMetadata am = new EntityMetadata().new AttributeMetadata();
			am.setName(attr.getName());
			Class<?> jt = attr.getJavaType();
			am.setClazz(jt.getName());
			am.setAssociation(attr.isAssociation());
			am.setPersistentType(attr.getPersistentAttributeType().name());
			am.setCollectionAttr(attr.isCollection());
			if(attr.isCollection()) {
				PluralAttribute collAttr = (PluralAttribute)attr;
				Class jt2 = collAttr.getBindableJavaType();
				if(attr.isAssociation()) {
					am.setEntity(entityManager.getMetamodel().entity(jt2).getName() );
					
				}else {
					am.setElementClazz(jt2.getName());
					if(jt2.isEnum()) {
						am.setEnumAttr(true);
						am.setAllowableValues(jt2.getEnumConstants());
					}
				}
					
			}else if(jt.isArray()){
				Class jt3 = jt.getComponentType();
				if(jt3.isEnum()) {
					am.setEnumAttr(true);
					am.setAllowableValues(jt3.getEnumConstants());
				}
				am.setClazz(jt3.getName()+"[]");
				Optional<EntityType<?>> etOpt = entities.stream().filter(et->et.getJavaType().equals(jt3)).findAny();
				if(etOpt.isPresent()) {
					am.setEntity(etOpt.get().getName());
				}
			}else {
				if(attr.isAssociation()) {
					am.setEntity(entityManager.getMetamodel().entity(jt).getName() );
					
				}
				if(jt.isEnum()) {
					am.setEnumAttr(true);
					am.setAllowableValues(jt.getEnumConstants());
				}
			}
			Field field = FieldUtils.getField(entityClass, attr.getJavaMember().getName(), true);
			if(field!=null) {
				ApiModelProperty apiModelProp = field.getAnnotation(ApiModelProperty.class);
				if(apiModelProp != null && StringUtils.isNotBlank(apiModelProp.value()))
					am.setApiModelProp(apiModelProp.value());
				
			}
			return am;
		
	}
}
